package cn.elegent.lock.strategy.consul;
import cn.elegent.lock.ELegentLock;
import cn.elegent.lock.config.ConsulLockConfig;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.kv.model.GetValue;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.kv.model.PutParams;
import com.ecwid.consul.v1.session.model.NewSession;
import com.ecwid.consul.v1.session.model.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * consul 分布式锁处理类
 *      name: true
 *
 */
@ConditionalOnProperty(prefix = "elegent.lock",name = "type",havingValue = "consul")
@Slf4j
public class ConsulLock implements ELegentLock {

    @Autowired
    private ConsulLockConfig consulLockConfig;

    private ConsulClient consulClient;

    @PostConstruct
    public void init(){
        consulClient = new ConsulClient(consulLockConfig.getHost(),consulLockConfig.getPort());
    }

    @Override
    public boolean tryLock(String lockName) {
        if(isLocked(lockName)){//如果被锁，直接返回false
            log.info("consul分布式锁没有被释放,名称:{}",lockName);
            return false;
        }
        log.info( "---------------------" + isLocked(lockName));
        try {
            return tryLock(lockName, consulLockConfig.getTtl());
        }catch (Exception e){
            return false;
        }
    }

    @Override
    public boolean lock(String lockName) {
        while (true){
            if(!isLocked(lockName)){   //如果没有被锁
                if(tryLock(lockName, consulLockConfig.getTtl())){ //尝试加锁
                    break;//一直到加锁成功才退出
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        return true;
    }

    @Override
    public boolean isLocked(String lockName) {

        Response<GetValue> kvValue = consulClient.getKVValue(lockName);
        if(kvValue.getValue()==null){
            return false;
        }else{
            return true;
        }
    }

    /**
     * 获取锁
     * @param lockName 锁的名称(key)
     * @param ttlSeconds 锁的超时时间
     * @return
     */
    private boolean tryLock(String lockName,int ttlSeconds){

        try {
            PutParams putParams = new PutParams();
            String sessionId = createSession(lockName, ttlSeconds);
            putParams.setAcquireSession(sessionId);
            //2.将构件好的LockContext存储在consul中
            String key = lockName.hashCode()+"";
            consulClient.setKVValue(key, sessionId);
            //加锁成功
            Boolean value = consulClient.setKVValue(lockName, "lock:" + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), putParams).getValue();
            if(value){
                //开起一个新的线程去执行锁续命--调用看门狗的方式实现
                if(consulLockConfig.isRenewal()){  //如果续期
                    new ConsulWatchDog(consulClient).watchSession(sessionId,ttlSeconds);//启动看门狗
                }
                log.info("consul加锁成功，锁名称：{}",lockName);
                return true;
            }else{
                //将sessionId删除
                consulClient.deleteKVValue(key);
                log.info("consul加锁失败，锁名称：{}",lockName);
                return false;
            }
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }



    /**
     * 释放锁
     */
    public boolean unLock(String lockName){
        try {
            //先根据锁名称找到对应的sessionId   session 是需要进行存储的  现在我们是通过一个lockContnt 假如说业务上不在一个微服务里释放锁 对应的 其他微服务如何获取到这把锁呢
            String key = lockName.hashCode() + "";
            String sessionId = consulClient.getKVValue(key).getValue().getDecodedValue();
            consulClient.sessionDestroy(sessionId, null);// key value也会被删除 是使用的DELETE
            //删除对应的 sessionId存储
            consulClient.deleteKVValue(key);
            log.info("consul释放锁成功，锁名称：{}",lockName);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            log.info("consul释放锁出错，锁名称：{}",lockName);
            return false;
        }
    }

    /**
     * 创建一个sessionId
     * @param lockName
     * @param ttlSeconds
     * @return
     */
    private String createSession(String lockName,int ttlSeconds){
        NewSession session = new NewSession();
        session.setBehavior(Session.Behavior.DELETE);
        session.setName("session " + lockName);
        session.setTtl(ttlSeconds + "s");
        return consulClient.sessionCreate(session, null).getValue();
    }

}
